package bytebuf

type ByteBuf struct {
	Pos         int
	Data        []byte
	IsBigEndian bool
}

func NewByteBuf() ByteBuf {
	return NewByteBufWith(0, make([]byte, 1024), false)
}

func NewByteBufWith(pos int, data []byte, bigEndian bool) ByteBuf {
	byteBuf := ByteBuf{Pos: pos, Data: data, IsBigEndian: bigEndian}
	return byteBuf
}

func (byteBuf *ByteBuf) Reset(data []byte, pos int) {
	byteBuf.Data = data
	byteBuf.Pos = pos
}

func (byteBuf *ByteBuf) ResetToPos(pos int) {
	byteBuf.Pos = pos
}

func (byteBuf *ByteBuf) GetCurrentData() []byte {
	return byteBuf.Data[:byteBuf.Pos]
}

func (byteBuf *ByteBuf) At(pos int) byte {
	return byteBuf.Data[pos]
}

func (byteBuf *ByteBuf) ReadByte() byte {
	return internalReadByte(byteBuf)
}

func (byteBuf *ByteBuf) ReadUint8() uint8 {
	return uint8(internalReadByte(byteBuf))
}

func (byteBuf *ByteBuf) ReadUint16() uint16 {
	return uint16(internalReadInt(byteBuf, 2))
}

func (byteBuf *ByteBuf) ReadIntWithLen2() {

}

func (byteBuf *ByteBuf) ReadUtf8WithLen1() string {
	return internalReadUtf8WithLenPrefix(byteBuf, 1)
}

func (byteBuf *ByteBuf) ReadUtf8WithLen2() string {
	return internalReadUtf8WithLenPrefix(byteBuf, 2)
}

func (byteBuf *ByteBuf) WriteUint8(value uint8) {
	ensureCapacity(byteBuf, 1)
	internalWriteUint8(byteBuf, value)
}

func (byteBuf *ByteBuf) WriteByte(value byte) {
	ensureCapacity(byteBuf, 1)
	internalWriteUint8(byteBuf, value)
}

func internalReadByte(byteBuf *ByteBuf) byte {
	b := byteBuf.Data[byteBuf.Pos]
	byteBuf.Pos++
	return b
}

func internalWriteUint8(byteBuf *ByteBuf, value uint8) {
	byteBuf.Data[byteBuf.Pos] = value
	byteBuf.Pos++
}

func internalWriteByte(byteBuf *ByteBuf, value byte) {
	byteBuf.Data[byteBuf.Pos] = value
	byteBuf.Pos++
}

func (byteBuf *ByteBuf) WriteUint16(value uint16) {
	ensureCapacity(byteBuf, 2)
	internalWriteUint16(byteBuf, value)
}

func internalWriteUint16(byteBuf *ByteBuf, value uint16) {
	if byteBuf.IsBigEndian {
		byteBuf.Data[byteBuf.Pos] = byte(value >> 8)
		byteBuf.Data[byteBuf.Pos+1] = byte(value)
	} else {
		byteBuf.Data[byteBuf.Pos] = byte(value)
		byteBuf.Data[byteBuf.Pos+1] = byte(value >> 8)
	}
	byteBuf.Pos += 2
}

func (byteBuf *ByteBuf) WriteUint64(value uint64) {
	ensureCapacity(byteBuf, 8)
	internalWriteUint64(byteBuf, value)
}

func (byteBuf *ByteBuf) WriteInt64(value int64) {
	ensureCapacity(byteBuf, 8)
	internalWriteInt64(byteBuf, value)
}

func (byteBuf *ByteBuf) WriteByteArray(value []byte) {
	ensureCapacity(byteBuf, len(value))
	internalWriteByteArray(byteBuf, value)
}

func internalWriteUint64(byteBuf *ByteBuf, value uint64) {
	if byteBuf.IsBigEndian {
		byteBuf.Data[byteBuf.Pos] = byte(value >> 56)
		byteBuf.Data[byteBuf.Pos+1] = byte(value >> 48)
		byteBuf.Data[byteBuf.Pos+2] = byte(value >> 40)
		byteBuf.Data[byteBuf.Pos+3] = byte(value >> 32)
		byteBuf.Data[byteBuf.Pos+4] = byte(value >> 24)
		byteBuf.Data[byteBuf.Pos+5] = byte(value >> 16)
		byteBuf.Data[byteBuf.Pos+6] = byte(value >> 8)
		byteBuf.Data[byteBuf.Pos+7] = byte(value)
	} else {
		byteBuf.Data[byteBuf.Pos] = byte(value)
		byteBuf.Data[byteBuf.Pos+1] = byte(value >> 8)
		byteBuf.Data[byteBuf.Pos+2] = byte(value >> 16)
		byteBuf.Data[byteBuf.Pos+3] = byte(value >> 24)
		byteBuf.Data[byteBuf.Pos+4] = byte(value >> 32)
		byteBuf.Data[byteBuf.Pos+5] = byte(value >> 40)
		byteBuf.Data[byteBuf.Pos+6] = byte(value >> 48)
		byteBuf.Data[byteBuf.Pos+7] = byte(value >> 56)
	}
	byteBuf.Pos += 8
}

func internalWriteInt64(byteBuf *ByteBuf, value int64) {
	if byteBuf.IsBigEndian {
		byteBuf.Data[byteBuf.Pos] = byte(value >> 56)
		byteBuf.Data[byteBuf.Pos+1] = byte(value >> 48)
		byteBuf.Data[byteBuf.Pos+2] = byte(value >> 40)
		byteBuf.Data[byteBuf.Pos+3] = byte(value >> 32)
		byteBuf.Data[byteBuf.Pos+4] = byte(value >> 24)
		byteBuf.Data[byteBuf.Pos+5] = byte(value >> 16)
		byteBuf.Data[byteBuf.Pos+6] = byte(value >> 8)
		byteBuf.Data[byteBuf.Pos+7] = byte(value)
	} else {
		byteBuf.Data[byteBuf.Pos] = byte(value)
		byteBuf.Data[byteBuf.Pos+1] = byte(value >> 8)
		byteBuf.Data[byteBuf.Pos+2] = byte(value >> 16)
		byteBuf.Data[byteBuf.Pos+3] = byte(value >> 24)
		byteBuf.Data[byteBuf.Pos+4] = byte(value >> 32)
		byteBuf.Data[byteBuf.Pos+5] = byte(value >> 40)
		byteBuf.Data[byteBuf.Pos+6] = byte(value >> 48)
		byteBuf.Data[byteBuf.Pos+7] = byte(value >> 56)
	}
	byteBuf.Pos += 8
}

func (byteBuf *ByteBuf) WriteUtf8String(value string) {
	bytes := []byte(value)
	ensureCapacity(byteBuf, len(bytes))
	internalWriteByteArray(byteBuf, bytes)
}

func (byteBuf *ByteBuf) WriteUtf8StringWithLen1(value string) {
	bytes := []byte(value)
	ensureCapacity(byteBuf, 1+len(bytes))
	internalWriteUint8(byteBuf, uint8(len(bytes)))
	internalWriteByteArray(byteBuf, bytes)
}

func (byteBuf *ByteBuf) WriteUtf8StringWithLen2(value string) {
	bytes := []byte(value)
	ensureCapacity(byteBuf, 2+len(bytes))
	internalWriteUint16(byteBuf, uint16(len(bytes)))
	internalWriteByteArray(byteBuf, bytes)
}

func internalWriteByteArray(byteBuf *ByteBuf, bytes []byte) {
	for _, s := range bytes {
		internalWriteByte(byteBuf, s)
	}
}

func internalReadInt(byteBuf *ByteBuf, count int) int {
	var result = 0
	if byteBuf.IsBigEndian {
		for i := byteBuf.Pos + count - 1; i >= byteBuf.Pos; i-- {
			result += int(byteBuf.Data[i] & 0x00FF)
		}
	} else {
		for i := byteBuf.Pos; i < byteBuf.Pos+count; i++ {
			result += int(byteBuf.Data[i] & 0x00FF)
		}
	}
	byteBuf.Pos += count
	return result
}

func internalReadIntWithLenPrefix(byteBuf *ByteBuf, lenCount int) int {
	l := internalReadInt(byteBuf, lenCount)
	return internalReadInt(byteBuf, l)
}

func internalReadUtf8(byteBuf *ByteBuf, count int) string {
	result := string(byteBuf.Data[byteBuf.Pos:byteBuf.Pos+count])
	byteBuf.Pos += count
	return result
}

func internalReadUtf8WithLenPrefix(byteBuf *ByteBuf, lenCount int) string {
	l := internalReadInt(byteBuf, lenCount)
	return internalReadUtf8(byteBuf, l)
}

func ensureCapacity(byteBuf *ByteBuf, extra int) {
	if byteBuf.Pos+extra > len(byteBuf.Data) {
		newCapacity := (byteBuf.Pos+extra)*4/3 + 1
		newData := make([]byte, newCapacity)
		copy(newData, byteBuf.Data)
		byteBuf.Data = newData
	}
}
